import { Vec2, v2 } from "cc";

export class MathUtils {
    /**
     * 生成指定范围的随机整数
     * @example
     * MathUtils.randomInt(1, 10); // 1~10 之间的整数
     */
    public static randomInt(min: number, max: number): number {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    /**
     * 保留小数点后 n 位
     * @example
     * MathUtils.toFixed(3.14159, 2); // 3.14
     */
    public static toFixed(num: number, fractionDigits: number): number {
        return parseFloat(num.toFixed(fractionDigits));
    }

    /**
     * 线性插值
     * @example
     * MathUtils.lerp(0, 10, 0.5); // 5
     */
    public static lerp(a: number, b: number, t: number): number {
        return a + (b - a) * t;
    }

    /**
     * 限制数值在区间内
     * @example
     * MathUtils.clamp(15, 0, 10); // 10
     */
    public static clamp(value: number, min: number, max: number): number {
        return Math.max(min, Math.min(max, value));
    }

    /**
     * 判断两个浮点数是否近似相等
     * @example
     * MathUtils.approximately(0.1 + 0.2, 0.3); // true
     */
    public static approximately(a: number, b: number, epsilon: number = 1e-6): boolean {
        return Math.abs(a - b) < epsilon;
    }
} 

/**
 * 用于生成随机数据的类。
 * @doc https://docs.unity.cn/cn/2019.4/ScriptReference/Random.html
 */
export default class Random {

    protected static _defaultRandom: Random;
    protected static get defaultRandom(): Random {
        if (!Random._defaultRandom) Random._defaultRandom = new Random();
        return Random._defaultRandom;
    }

    /**返回半径为 1 的圆形内的随机点（只读）。 */
    public static get insideUnitCircle(): Vec2 {
        return Random.defaultRandom.insideUnitCircle;
    }

    /**
     * 返回半径为1，圆边随机点
     */
    public static get onUnitCircle(): Vec2 {
        return Random.defaultRandom.onUnitCircle;
    }

    /**
     * 返回介于 0.0 [含] 与 1.0 [含] 之间的随机浮点数。
     */
    public static get value(): number {
        return Random.defaultRandom.value;
    }

    /**
     * 返回介于 min [含] 与 max [含] 之间的随机整型,浮点数使用floatRange
     * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random.Range.html
     */
    public static range(min: number, max: number): number {
        return Random.defaultRandom.range(min, max);
    }

    /**
    * 返回介于 min [含] 与 max [含] 之间的随机浮点数,整型使用range
    * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random.Range.html
    */
    public static floatRange(min: number, max: number): number {
        return Random.defaultRandom.floatRange(min, max);
    }


    protected seed: number;
    constructor(seed?: number) {
        this.seed = seed || Date.now();
    }

    /**
     * 返回介于 min [含] 与 max [含] 之间的随机浮点数,整型使用range
     * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random.Range.html
     */
    public floatRange(min: number, max: number): number {
        max = max || 0;
        min = min || 0;
        max = Math.max(max, min);
        min = Math.min(max, min);

        this.seed = (this.seed * 9301 + 49297) % 233280;
        const rnd = this.seed / (233280.0);
        return min + rnd * (max - min);
    }

    /**
     * 返回介于 min [含] 与 max [含] 之间的随机整型,浮点数使用floatRange
     * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random.Range.html
     */
    public range(min: number, max: number): number {
        return Math.round(this.floatRange(min, max));
    }

    /**
     * 返回半径为 1 的圆形内的随机点
     * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random-insideUnitCircle.html
     */
    public get insideUnitCircle(): Vec2 {
        const angle = this.floatRange(0, 360);
        const r = this.floatRange(0, 1);
        return v2(r * Math.sin(angle * Math.PI / 180), r * Math.cos(angle * Math.PI / 180));
    }

    /**
     * 返回半径为1，圆边随机点
     */
    public get onUnitCircle(): Vec2 {
        const angle = this.floatRange(0, 360);
        return v2(Math.sin(angle * Math.PI / 180), Math.cos(angle * Math.PI / 180));
    }

    /**
    * 返回介于 0.0 [含] 与 1.0 [含] 之间的随机浮点数
    * @link https://docs.unity.cn/cn/2019.4/ScriptReference/Random-value.html
    */
    public get value(): number {
        return this.floatRange(0, 1);
    }

}